﻿using System;
using System.IO;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;

namespace HardStyle.SimpleRSAUtil
{
    /// <summary>
    /// Used to generate RSA key pair
    /// </summary>
    public static class RSAKeyGenerator
    {
        /// <summary>
        /// Generate a set of RSA keys
        /// </summary>
        /// <param name="dwKeySize"></param>
        /// <returns></returns>
        public static (string publicKey, string privateKey) GenerateRSAKeys(int dwKeySize = 2048)
        {
            RsaKeyPairGenerator rsaKeyPairGenerator = new RsaKeyPairGenerator();
            rsaKeyPairGenerator.Init(new KeyGenerationParameters(new SecureRandom(), dwKeySize));
            AsymmetricCipherKeyPair asymmetricCipherKeyPair = rsaKeyPairGenerator.GenerateKeyPair();

            TextWriter privateTextWriter = new StringWriter();
            PemWriter privatePemWriter = new PemWriter(privateTextWriter);
            privatePemWriter.WriteObject(asymmetricCipherKeyPair.Private);
            privatePemWriter.Writer.Flush();

            TextWriter publicTextWriter = new StringWriter();
            PemWriter publicPemWriter = new PemWriter(publicTextWriter);
            publicPemWriter.WriteObject(asymmetricCipherKeyPair.Public);
            publicPemWriter.Writer.Flush();

            var publicKey = publicTextWriter.ToString();
            var privateKey = privateTextWriter.ToString();

            #region 转pkcs8
            var reader = new StringReader(privateKey);
            var prKey = ((AsymmetricCipherKeyPair)new PemReader(reader).ReadObject()).Private;
            var pkcs8Generator = new Pkcs8Generator(prKey);
            var pkcs8_privateKeyObject = pkcs8Generator.Generate();
            privateKey = Convert.ToBase64String(pkcs8_privateKeyObject.Content);
            #endregion


            if (string.IsNullOrEmpty(publicKey) || string.IsNullOrEmpty(privateKey))
            {
                throw new ArgumentNullException("RSAKeys Generate fail");
            }

            return (publicKey, privateKey);
        }


        /// <summary>
        /// Generate a set of RSA keys in the specified format
        /// </summary>
        /// <param name="dwKeySize"></param>
        /// <param name="isprivateKeyXml">Whether it is an XML format private key</param>
        /// <param name="isPublicKeyXml">Whether it is an XML format public key</param>
        /// <returns></returns>
        public static (string publicKey, string privateKey) GenerateXmlRSAKeys(int dwKeySize = 2048, bool isprivateKeyXml = true, bool isPublicKeyXml = true)
        {
            var (pubKey, priKey) = GenerateRSAKeys(dwKeySize);

            if (isPublicKeyXml is true)
            {
                pubKey = RSAKeyConvert.RsaPublicKeyJava2DotNet(pubKey);
            }

            if (isprivateKeyXml is true)
            {
                priKey = RSAKeyConvert.RsaPrivateKeyJava2DotNet(priKey);
            }

            return (pubKey, priKey);
        }


        /// <summary>
        /// Generate a key pair and save it to the specified path
        /// </summary>
        /// <param name="dwKeySize"></param>
        /// <param name="isprivateKeyXml">Whether it is an XML format private key</param>
        /// <param name="isPublicKeyXml">Whether it is an XML format public key</param>
        /// <param name="saveKeyDirectory">Folder where the key is saved</param>
        /// <param name="overwrite">Overwrite the existing key under the path</param>
        /// <exception cref="Exception"></exception>
        public static void PersistenceGenerationRSAKeys(int dwKeySize, bool isprivateKeyXml, bool isPublicKeyXml,
            string saveKeyDirectory, bool overwrite = false)
        {
            var prKeyFileName = saveKeyDirectory + "/private.key";
            var puKeyFileName = saveKeyDirectory + "/public.key";
            if (overwrite is false)
            {
                if (File.Exists(prKeyFileName) || File.Exists(puKeyFileName))
                {
                    throw new Exception("Key already exists, please try to change the generation path");
                }
            }

            if (Directory.Exists(saveKeyDirectory) is false)
            {
                Directory.CreateDirectory(saveKeyDirectory);
            }

            var (pubKey, priKey) = GenerateXmlRSAKeys(dwKeySize, isprivateKeyXml, isPublicKeyXml);
            using var pubWriter = File.CreateText(puKeyFileName);
            pubWriter.Write(pubKey);
            pubWriter.Flush();
            using var priWriter = File.CreateText(prKeyFileName);
            priWriter.Write(priKey);
            priWriter.Flush();
        }


    }

}
